//
// Created by LiuYou on 2021/8/5.
//

#include <cstdio>
#include <cstdlib>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctime>
#include <sys/wait.h>
#include "Protocol.hpp"


static void serverJob(int fd) {
    const int BUF_SIZE = 1024;
    char buf[BUF_SIZE]{};
    // sprintf()函数返回值是真正的操作的字节数
    int len = sprintf( buf, FMT_STAMP, static_cast<long long>(time( nullptr )));
    // send()中len是实际要发送的数据的长度。
    ssize_t ret = send( fd, buf, len, 0 );
    if (ret < 0) {
        perror( "send()" );
        exit( 1 );
    }
}


static void serverLoop(int socketFd, sockaddr_in& remoteAddress, socklen_t& addressLen) {
    while (true) {
        int acceptFd = accept( socketFd, (sockaddr*) &remoteAddress, &addressLen );
        if (acceptFd < 0) {
            perror( "accept()" );
            exit( 1 );
        }
        socklen_t ipStrSize = 40;
        char ipStr[ipStrSize];
        inet_ntop( AF_INET, &remoteAddress.sin_addr, ipStr, ipStrSize );
        fprintf( stdout, "[%d] Client:%s:%d\n",
                 getpid(), ipStr, ntohs( remoteAddress.sin_port ));

        serverJob( acceptFd );
        close( acceptFd );
    }
}


int main() {
    int socketFd = socket( PF_INET, SOCK_STREAM, 0 );
    if (socketFd < 0) {
        perror( "socket()" );
        exit( 1 );
    }

    // 处理异常终止, 端口没有立刻释放的问题。这样就可以下一次继续bind()这个端口。
    int optVal = 1;
    if (setsockopt( socketFd, SOL_SOCKET, SO_REUSEADDR, &optVal, sizeof( optVal )) < 0) {
        perror( "setsockopt()" );
        exit( 1 );
    }

    struct sockaddr_in localAddress{
            .sin_family=AF_INET,
            .sin_port=htons( atoi( SERVER_PORT )),
    };
    inet_pton( AF_INET, "0.0.0.0", &localAddress.sin_addr );
    int ret = bind( socketFd, (sockaddr*) &localAddress, sizeof( localAddress ));
    if (ret < 0) {
        perror( "bind()" );
        exit( 1 );
    }

    ret = listen( socketFd, 200 );
    if (ret < 0) {
        perror( "listen()" );
        exit( 1 );
    }

    struct sockaddr_in remoteAddress;
    socklen_t addressLen = sizeof( remoteAddress );


    const int PROC_NUM = 4;
    for (int i = 0; i < PROC_NUM; ++i) {
        pid_t pid = fork();
        if (pid < 0) {
            perror( "fork()" );
            exit( 1 );
        }
        if (pid == 0) {
            serverLoop( socketFd, remoteAddress, addressLen );
            exit( 0 );
        }

    }
    for (int i = 0; i < PROC_NUM; ++i) {
        wait( nullptr );
        close( socketFd );
        exit( 0 );
    }
}
